Support full uint range for body length #638
Closed
+39
−31
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Describe your changes
Context
The Bug
This is what I am seeing for the failing file. Everything goes fine up until we hit the literal data packet piece of the pgp file. The following screenshots are from when we start processing the literal data packet.
The first byte of the header shows that it is a long length packet (b0 = 255). Note that in this case, the header length is 6 bytes, so after this first byte, we have 5 to read still. Also, I have changed the code ever so slightly in the screenshots below just so I could see the numbers at each step--no functionality changes were made though.
If I step into the RequireUInt32BE() method, I get the following:
Where you can see that the bytes of the header are properly read and give us 4082119084, which if you refer back to the packet screenshot above, corresponds exactly with the packet length (plen).
We now return back to the ReadBodyLen method where we started with this as the results
The response variable lines up with what we see as the packet length from the previous step. However, the code then casts this UInt as an int, which results in an overflow, and we get a negative number. Again, I have some extra variables here just to highlight the difference in values in each step of the process.
This negative number goes back to the calling method, RequireBodyLen(), which then throws an error because the body length was negative.
However, I don’t believe the EndOfStream exception is even accurate here because, from what I can tell, we did not read past the end of the stream. We were simply reading header bytes to determine the length of the literal packet content that follows the header.
According to the PGP spec 4880 this large size is not a violation of the rules, so I would expect it to work (and based on debugging the BouncyCastle code it seems totally fine reading the headers and getting the correct length value until it tries to cast it as an int). The problem is that the file length is simply too big to read into an int, and as marked by this TODO, BouncyCastle needs to support UInt so that large files can be processed.
The Fix
This pull request modifies StreamUtilities.cs ReadBodyLen method to return a
uint?instead of aint. If the length is invalid for any reason, it returnsnull, otherwise it returns the proper size represented in theuintrange. All other file changes are either to make the compiler happy or to enforce that partial data packets have a max size of2**30as per the spec, even though the uint range can support up to2**32 - 1.How has this been tested?
The
bc-test-dataGitHub repository was cloned and all tests were ran in thetestfolder and successfully passed.Checklist before requesting a review
See also Contributing Guidelines.